package com.wa.es.my;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Console;
import com.wa.es.constant.ElasticQueryConstant;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.search.MatchQuery;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;
import java.util.Set;

/**
 * @author gary
 * @date 2021年12月14日 17:03
 */
public class BoolQueryBuilderTest {

    static RestHighLevelClient highLevelClient;

    private String productIndex = "product_alias_test";
    private String wareIndex = "entity_partnum_test_v5";

    @Before
    public void getClient() throws Exception {
        // Low Level Client init
        highLevelClient = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.1.105", 9200, "http")));
    }

    @After
    public void close() throws Exception {
        // Low Level Client init
        highLevelClient.close();
    }

    public static SearchResponse getSearchHits(String indexName, SearchSourceBuilder searchSourceBuilder){
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchSourceBuilder.trackTotalHits(Boolean.TRUE);

        //https://www.elastic.co/guide/cn/elasticsearch/guide/current/relevance-intro.html#explain
        //输出 explain 结果代价是十分昂贵的，它只能用作调试工具 。千万不要用于生产环境。
        //builder.setExplain(true);
        Console.log("index name:{}",indexName);
        Console.log("执行查询语句:{}",searchSourceBuilder.toString());
        searchRequest.source(searchSourceBuilder);

        return getResponseData(searchRequest);
    }

    /**
     * 执行查询
     * @param searchRequest
     * @return
     */
    public static SearchResponse getResponseData(SearchRequest searchRequest) {
        SearchResponse searchResponse = null;
        try {
            long startTime = System.currentTimeMillis();
            searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            long endTime = System.currentTimeMillis();
            Console.log("执行搜索查询时间:{}",(endTime-startTime));
        } catch (Exception e) {
            Console.log("执行查询异常{}",e);
            e.printStackTrace();
        }
        return searchResponse;
    }


    /**
     *  存在minimumShouldMatch,需要满足条件才能出结果
     * 1.minimum_should_match:"3"          无论可选子句的数量如何，都表示固定值.
     * 2.minimum_should_match:"-2"         表示可选子句的总数减去此数字应该是必需的。
     * 3.minimum_should_match:"75%"
     *    表示最少匹配的子句个数,例如有五个可选子句,最少的匹配个数为5*75%=3.75.向下取整为3,这就表示五个子句最少要匹配其中三个才能查到.
     * 4.minimum_should_match:"-25%"
     *    和上面的类似,只是计算方式不一样,假如也是五个子句,5*25%=1.25,向下取整为1,5最少匹配个数为5-1=4.
     * 5.minimum_should_match:"3<90%"
     *    表示如果可选子句的数量等于（或小于）设置的值，则它们都是必需的，但如果它大于设置的值，则适用规范。在这个例子中：如果有1到3个子句，则它们都是必需的，但是对于4个或更多子句，只需要90％的匹配度.
     */
    @Test
    public void minimumShouldMatch(){
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        List<QueryBuilder> filterList = boolQuery.filter();
        //型号
        TermsQueryBuilder partNumberTermQueryBuilder = QueryBuilders.termsQuery("partNumber.rawPartNumber", ListUtil.toList("MCN12FD330J","06035A331FAT2A"));
        filterList.add(partNumberTermQueryBuilder);

        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("standardBrandId",ListUtil.toList("1","2","3"));
        termsQueryBuilder.boost(1.0F);
        boolQuery.should(termsQueryBuilder);

        TermsQueryBuilder termsQueryBuilder2 = QueryBuilders.termsQuery("standardBrandId",ListUtil.toList("4","5"));
        termsQueryBuilder.boost(1.0F);
        boolQuery.should(termsQueryBuilder2);

        //必须要存在一个should满足条件才符合，去掉可以不需要满足条件
        boolQuery.minimumShouldMatch(1);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQuery);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     * 外层must，内存should
     */
    @Test
    public void boolMustNestBoolShouldMatchPhraseQuery(){
        //外层bool
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        Set<String> capCategory = CollUtil.newHashSet("贴片电解电容","直插电解电容","铝电解电容");
        //内层bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        for (String s : capCategory) {
            MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(ElasticQueryConstant.CATEGORY_NAME_SEPARATE,s);
            matchPhraseQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_BUILD_IN_WHITESPACE);
            matchPhraseQueryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE);
            boolQueryBuilder.should(matchPhraseQueryBuilder);
        }
        //必须满足一个才能出结果, 不设置结果一致
        boolQueryBuilder.minimumShouldMatch(1);

        //必须存在
        boolQuery.must(boolQueryBuilder);
        //设置成should结果一致
//        boolQuery.should(boolQueryBuilder);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQuery);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }


    /**
     *  如果只有should,条件要满足
     */
    @Test
    public void boolShouldMatchQuery(){
        //外层bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(ElasticQueryConstant.PARAM_DESC,"0603");
        matchQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_WARE_DESC);
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("1");
        matchQueryBuilder.boost(1);
        matchQueryBuilder.operator(Operator.OR);
        boolQueryBuilder.should(matchQueryBuilder);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = getSearchHits(wareIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  如果存在must，should没有写minimumShouldMatch,不需要满足
     */
    @Test
    public void boolMustTermShouldMatcHQuery(){
        //外层bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        TermQueryBuilder teamTermQueryBuilder = QueryBuilders.termQuery("brandId", 577);
        boolQueryBuilder.must(teamTermQueryBuilder);

        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(ElasticQueryConstant.PARAM_DESC,"0603");
        matchQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_WARE_DESC);
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("1");
        matchQueryBuilder.boost(1);
        matchQueryBuilder.operator(Operator.OR);
        boolQueryBuilder.should(matchQueryBuilder);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = getSearchHits(wareIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

}
